package com.momo.rabbitsingleconsumer.rabbitmq.dead;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.momo.rabbitsingleconsumer.entity.JSONResult;
import com.rabbitmq.client.Channel;
import lombok.Value;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.stereotype.Component;

/**
 * @program: momo-cloud
 * @description: 订单(死信)队列----》消息接收
 * 在以下3种情况会进入死信队列
 * 1.有消息被拒绝（basic.reject/ basic.nack）并且requeue=false
 * 2.队列达到最大长度
 * 3.消息TTL过期
 * arguments        键	值	意义
 * x-message-ttl	数字类型，标志时间，以豪秒为单位	标志队列中的消息存活时间，也就是说队列中的消息超过了制定时间会被删除
 * x-expires	数字类型，标志时间，以豪秒为单位	队列自身的空闲存活时间，当前的queue在指定的时间内，没有consumer、basic.get也就是未被访问，就会被删除。
 * x-max-length和x-max-length-bytes	数字	最大长度和最大占用空间，设置了最大长度的队列，在超过了最大长度后进行插入会删除之前插入的消息为本次的留出空间,相应的最大占用大小也是这个道理，当超过了这个大小的时候，会删除之前插入的消息为本次的留出空间。
 * x-dead-letter-exchange和x-dead-letter-routing-key	字符串	消息因为超时或超过限制在队列里消失，这样我们就丢失了一些消息，也许里面就有一些是我们做需要获知的。而rabbitmq的死信功能则为我们带来了解决方案。设置了dead letter exchange与dead letter routingkey（要么都设定，要么都不设定）那些因为超时或超出限制而被删除的消息会被推动到我们设置的exchange中，再根据routingkey推到queue中
 * x-max-priority	数字	队列所支持的优先级别，列如设置为5，表示队列支持0到5六个优先级别，5最高，0最低，当然这需要生产者在发送消息时指定消息的优先级别，消息按照优先级别从高到低的顺序分发给消费者
 * alternate-exchange		下面简称AE，当一个消息不能被route的时候，如果exchange设定了AE，则消息会被投递到AE。如果存在AE链，则会按此继续投递，直到消息被route或AE链结束或遇到已经尝试route过消息的AE。
 *
 * @author: Jie Li
 * @create: 2019-07-27 14:58
 **/
@Component
@Slf4j
public class TopicOrderDeadSingleRabbitReceiver {


    @RabbitListener(bindings = {@QueueBinding(
            value = @Queue(value = DeadSingelRabbitConfig.QUEUE,//队列名称
                    durable = DeadSingelRabbitConfig.DERABLE,
                    arguments = {//  将普通队列绑定到死信队列交换机上
                            //之前的队列没有绑定死信队列和死信交换机 不能做更改绑定死信交互机
                            //之前创建好的邮件队列 删除掉  已经创建好的队列不能做更改  交换机也清理掉
                            @Argument(name = DeadSingelRabbitConfig.DEAD_LETTER_QUEUE_KEY, value = DeadSingelRabbitConfig.deadExchangeName), //死信交换机
                            @Argument(name = DeadSingelRabbitConfig.DEAD_LETTER_ROUTING_KEY, value = DeadSingelRabbitConfig.deadRoutingKey)}),//是否持久化
            exchange = @Exchange(value = DeadSingelRabbitConfig.EXCHANGE, //交换机名称
                    durable = DeadSingelRabbitConfig.DERABLE,//是否持久化
                    type = DeadSingelRabbitConfig.TYPE, //消息路由规则
                    ignoreDeclarationExceptions = DeadSingelRabbitConfig.IGNOREDECEXCEPTION), // 忽略声明异常
            key = DeadSingelRabbitConfig.KEY // 绑定的路由键或模式。
    )}
    )
    @RabbitHandler
    public void onMessage(Message message, Channel channel) throws Exception {
        String allMsg = "";
        try {
            allMsg = JSONObject.toJSONString(message);
            log.info("订单消费者收到消息载体：{}", allMsg);
            String msg = new String(message.getBody());
            JSONResult jsonResult = JSON.parseObject(msg, new TypeReference<JSONResult>() {
            });
            log.info("订单消费者收到消息msg：{}", msg);
            String messageId = message.getMessageProperties().getMessageId();
            long deliveryTag = message.getMessageProperties().getDeliveryTag();
            log.info("订单消费者messageId:{},deliveryTag:{}", messageId, deliveryTag);
            System.out.println(1 / 0);
            //手工ACK
            //第一个deliveryTag参数为每条信息带有的tag值，第二个multiple参数为布尔类型
            //为true时会将小于等于此次tag的所有消息都确认掉，如果为false则只确认当前tag的信息，可根据实际情况进行选择。
            //恢复消息确认成功
            channel.basicAck(deliveryTag, false);
        } catch (Exception e) {
            //拒绝消费消息（丢失消息） 给死信队列
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
            //重新分配(消费者会一直消费这条消息，直到消费成功)
//            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
            e.printStackTrace();
            log.error("订单消费者 消费异常:{},{}", allMsg, e.getMessage());
        }

    }
}
